package math

import (
	"math/rand"
	"time"

	"golang.org/x/exp/constraints"
)

var (
	_t time.Time
	_r *rand.Rand
)

func init() {
	_t = time.Now()
	_r = rand.New(rand.NewSource(_t.UnixNano()))
}
func getRand() *rand.Rand {
	r := _r
	if time.Since(_t) > time.Minute {
		//next use new rand
		_t = time.Now()
		_r = rand.New(rand.NewSource(_t.UnixNano()))
	}
	return r
}

// [min,max]
func RandInt(min int, max int) int {
	if max < min {
		return RandInt(max, min)
	}
	r := getRand()
	return min + r.Intn(max-min+1)
}

// 概率随机
func RandTrue(odds int, base int) bool {
	if odds >= base {
		return true
	}
	return RandInt(0, base) < odds
}

// 概率随机[0,1)
func RandBool(odds float64) bool {
	r := getRand()
	return r.Float64() < odds
}

// [min,max]
func RandI[T constraints.Integer](min, max T) T {
	if max < min {
		min, max = max, min
	}
	r := getRand()
	return randI(r, min, max)
}

// [min, max)
func RandF[T constraints.Float](min, max T) T {
	r := getRand()
	return min + T(r.Float64())*(max-min)
}

func RandDistribute[T constraints.Integer](total T, count int, deviation T) []T {
	if count < 2 {
		return []T{total}
	}
	ret := make([]T, count)
	r := getRand()
	mid := total / T(count)
	if total > 0 {
		min := mid - deviation
		max := mid + deviation
		if mid < deviation {
			min = 0
		}
		for i := 0; i < count-1; i++ {
			ret[i] = randI(r, min, max)
			total -= ret[i]
			m := total / (T(count - i - 1))
			if m <= min {
				total += ret[i]
				for j := i; j < count-1; j++ {
					ret[j] = min
					total -= min
				}
				break
			} else if m >= max {
				total += ret[i]
				for j := i; j < count-1; j++ {
					ret[j] = max
					total -= max
				}
			}
		}
		ret[count-1] = total
	} else {
		remain := total % T(count)
		i := 0
		for remain < 0 {
			ret[i] = mid - 1
			remain++
			i++
		}
		for i < count {
			ret[i] = mid
			i++
		}
	}
	r.Shuffle(count, func(i, j int) {
		ret[i], ret[j] = ret[j], ret[i]
	})
	return ret
}

func randI[T constraints.Integer](r *rand.Rand, min, max T) T {
	return min + T(r.Int63n(int64(max-min+1)))
}
